<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja" lang="ja">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<link href="../css/main.css" media="all" rel="stylesheet" type="text/css" />
<link href="../css/highlight.css" media="all" rel="stylesheet" type="text/css" />
<link href="../css/print.css" media="print" rel="stylesheet" type="text/css" />
<title>第2章 - symfonyのコードを探求する</title>
</head>

<body>
<div class="navigation">

<table width="100%">
<tr>
<td width="40%" align="left"><a href="01-Introducing-Symfony.html">前の章</a></td>
<td width="20%" align="center"><a href="index.html">ホーム</a></td>
<td width="40%" align="right"><a href="03-Running-Symfony.html">次の章</a></td>
</tr>
</table>
<hr/>
</div>

<div>
<a name="chapter.2.exploring.symfonys.code" id="chapter.2.exploring.symfonys.code"></a><h1>第2章 - symfonyのコードを探求する</h1>

<p>一見すると、symfony駆動のアプリケーションの背後に存在する(巨大で複雑な)コードにやる気をくじかれてしまうかもしれません。symfonyは多くのディレクトリとスクリプトで構成され、PHPクラスファイルとHTMLファイルが混在しており、ファイルのなかには両方の言語が含まれるものもあります。クラスのリファレンスがアプリケーションのフォルダーのなかでは見つからず、ディレクトリの深さが6レベルに達していることも見ることになります。しかしながら、ひとたびこの見た目の複雑のすべての背後にある理由を理解したのであれば、symfonyのアプリケーション構造がごく自然であり別のものに置き換えようとは思わなくなるでしょう。この章ではおびえた感情を説明でとり除きます。</p>

<div class="toc">
<dl>
<dt><a href="#the.mvc.pattern">2.1. MVCパターン</a></dt>
<dd><dl>
<dt><a href="#mvc.layering">2.1.1. MVC階層化</a></dt>
<dd><dl>
<dt><a href="#flat.programming">2.1.1.1. ベタ書きのプログラミング</a></dt>
<dt><a href="#isolating.the.presentation">2.1.1.2. プレゼンテーションを分離する</a></dt>
<dt><a href="#isolating.the.data.manipulation">2.1.1.3. データの操作機能を分離する</a></dt>
</dl></dd>
<dt><a href="#layer.separation.beyond.mvc">2.1.2. MVCを超えるレイヤーの分離</a></dt>
<dd><dl>
<dt><a href="#database.abstraction">2.1.2.1. データベースの抽象化</a></dt>
<dt><a href="#view.elements">2.1.2.2. ビューの要素</a></dt>
<dt><a href="#action.and.front.controller">2.1.2.3. アクションとフロントコントローラー</a></dt>
<dt><a href="#object.orientation">2.1.2.4. オブジェクト指向</a></dt>
</dl></dd>
<dt><a href="#symfonys.mvc.implementation">2.1.3. symfonyによるMVCの実装方法</a></dt>
<dt><a href="#symfony.core.classes">2.1.4. symfonyのコアクラス</a></dt>
</dl></dd>
<dt><a href="#code.organization">2.2. コードの編成</a></dt>
<dd><dl>
<dt><a href="#project.structure.applications.modules.and.actions">2.2.1. プロジェクトの構造: アプリケーション、モデルとアクション</a></dt>
<dt><a href="#file.tree.structure">2.2.2. ファイルのツリー構造</a></dt>
<dd><dl>
<dt><a href="#root.tree.structure">2.2.2.1. rootのツリー構造</a></dt>
<dt><a href="#application.tree.structure">2.2.2.2. アプリケーションのツリー構造</a></dt>
<dt><a href="#module.tree.structure">2.2.2.3. モジュールのツリー構造</a></dt>
<dt><a href="#web.tree.structure">2.2.2.4. webディレクトリのツリー構造</a></dt>
</dl></dd></dl></dd>
<dt><a href="#common.instruments">2.3. 共通の手法</a></dt>
<dd><dl>
<dt><a href="#parameter.holders">2.3.1. パラメーターホルダー</a></dt>
<dt><a href="#constants">2.3.2. 定数</a></dt>
<dt><a href="#class.autoloading">2.3.3. クラスのオートロード機能</a></dt>
</dl></dd>
<dt><a href="#summary">2.4. まとめ</a></dt>
</dl>
</div>
<a name="the.mvc.pattern" id="the.mvc.pattern"></a><h2>MVCパターン</h2>

<p>symfonyは3つのレベルから構成される、MVCアーキテクチャ(Model-View-Controller architecture)として知られるWebの古典的なデザインパターンに基づいています:</p>

<ul>
<li>モデル(Model)はアプリケーションが影響を与える情報、ビジネスロジックを表します。</li>
<li>ビュー(View)はモデルをユーザーとのインタラクションに最適なWebページにレンダリングします。</li>
<li>コントローラー(Controller)はユーザーのアクションに対応しモデルもしくはビューの変更を適切に行います。</li>
</ul>

<p>図2-1はMVCパターンを図示しています。</p>

<p>MVCアーキテクチャはビジネスロジック(モデル)とプレゼンテーションを分離するので、結果として維持管理しやすくなります。たとえば、アプリケーションが標準的なWebブラウザーと携帯端末の両方で動く場合、新しいビューだけが必要です; オリジナルのコントローラーとモデルはそのままにできます。コントローラーはモデルとビューからリクエスト(HTTP、コンソールモード、メールなど)のために使われるプロトコルの詳細を隠すための助けを行います。そしてモデルはデータのロジックの抽象化を行い、ビューとアクションを、たとえば、アプリケーションによって使われるデータベースの種類から独立したものにします。</p>

<p>図2-1 - MVCパターン</p>

<p><img src="images/F0201.png" alt="MVCパターン" title="MVCパターン" /></p>

<a name="mvc.layering" id="mvc.layering"></a><h3>MVC階層化</h3>

<p>MVCの利点を理解するために、PHPの基本アプリケーションをMVCアーキテクチャのアプリケーションに改造する方法を見てみましょう。blogアプリケーションにおける投稿の一覧を表示する機能が完璧な例です。</p>

<a name="flat.programming" id="flat.programming"></a><h4>ベタ書きのプログラミング</h4>

<p>ベタ書きのPHPファイル(flat PHP file)において、データベースのエントリーの一覧表示機能はリスト2-1のようなスクリプトになります。</p>

<p>リスト2-1 - ベタ書きのスクリプト</p>

<pre class="php"><span class="kw2">&lt;?php</span>
&nbsp;
<span class="co1">//データベースに接続、選択する</span>
<span class="re0">$link</span> <span class="sy0">=</span> <span class="kw3">mysql_connect</span><span class="br0">&#40;</span><span class="st_h">'localhost'</span><span class="sy0">,</span> <span class="st_h">'myuser'</span><span class="sy0">,</span> <span class="st_h">'mypassword'</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="kw3">mysql_select_db</span><span class="br0">&#40;</span><span class="st_h">'blog_db'</span><span class="sy0">,</span> <span class="re0">$link</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
<span class="co1">// SQLクエリを実行する</span>
<span class="re0">$result</span> <span class="sy0">=</span> <span class="kw3">mysql_query</span><span class="br0">&#40;</span><span class="st_h">'SELECT date, title FROM post'</span><span class="sy0">,</span> <span class="re0">$link</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
<span class="sy1">?&gt;</span>
&nbsp;
&lt;html&gt;
  &lt;head&gt;
    &lt;title&gt;投稿の一覧&lt;/title&gt;
  &lt;/head&gt;
  &lt;body&gt;
   &lt;h1&gt;投稿の一覧&lt;/h1&gt;
   &lt;table&gt;
     &lt;tr&gt;&lt;th&gt;日付&lt;/th&gt;&lt;th&gt;タイトル&lt;/th&gt;&lt;/tr&gt;
<span class="kw2">&lt;?php</span>
<span class="co1">// 結果をHTML形式で出力する</span>
<span class="kw1">while</span> <span class="br0">&#40;</span><span class="re0">$row</span> <span class="sy0">=</span> <span class="kw3">mysql_fetch_array</span><span class="br0">&#40;</span><span class="re0">$result</span><span class="sy0">,</span> MYSQL_ASSOC<span class="br0">&#41;</span><span class="br0">&#41;</span>
<span class="br0">&#123;</span>
<span class="kw1">echo</span> <span class="st0">&quot;<span class="es1">\t</span>&lt;tr&gt;<span class="es1">\n</span>&quot;</span><span class="sy0">;</span>
<span class="kw3">printf</span><span class="br0">&#40;</span><span class="st0">&quot;<span class="es1">\t</span><span class="es1">\t</span>&lt;td&gt; <span class="es6">%s</span> &lt;/td&gt;<span class="es1">\n</span>&quot;</span><span class="sy0">,</span> <span class="re0">$row</span><span class="br0">&#91;</span><span class="st_h">'date'</span><span class="br0">&#93;</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="kw3">printf</span><span class="br0">&#40;</span><span class="st0">&quot;<span class="es1">\t</span><span class="es1">\t</span>&lt;td&gt; <span class="es6">%s</span> &lt;/td&gt;<span class="es1">\n</span>&quot;</span><span class="sy0">,</span> <span class="re0">$row</span><span class="br0">&#91;</span><span class="st_h">'title'</span><span class="br0">&#93;</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="kw1">echo</span> <span class="st0">&quot;<span class="es1">\t</span>&lt;/tr&gt;<span class="es1">\n</span>&quot;</span><span class="sy0">;</span>
<span class="br0">&#125;</span>
<span class="sy1">?&gt;</span>
    &lt;/table&gt;
  &lt;/body&gt;
&lt;/html&gt;
&nbsp;
<span class="kw2">&lt;?php</span>
&nbsp;
<span class="co1">// 接続を閉じる</span>
<span class="kw3">mysql_close</span><span class="br0">&#40;</span><span class="re0">$link</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
<span class="sy1">?&gt;</span></pre>

<p>速く書けてすぐに実行できますが、維持するのは不可能です。このコードの重大な問題はつぎのとおりです:</p>

<ul>
<li>エラーのチェック機能が存在しない(データベースの接続が失敗したらどうする？)</li>
<li>HTMLとPHPのコードが混在している。</li>
<li>コードがMySQLデータベースに直結している</li>
</ul>

<a name="isolating.the.presentation" id="isolating.the.presentation"></a><h4>プレゼンテーションを分離する</h4>

<p>リスト2-1の<code>echo</code>と<code>printf</code>の呼び出しがコードを読みづらくしています。現在の構文ではプレゼンテーションを強化するためにHTMLコードを修正する作業は骨が折れます。ですのでコードを2つの部分に分割する方法を採用することにします。最初に、リスト2-2で示すように、ビジネスロジックをともなう純粋なPHPコードをコントローラースクリプトに移動させます。</p>

<p>リスト2-2 - コントローラー部分を表す<code>index.php</code></p>

<pre class="php"> <span class="co1">// データベースに接続、選択する</span>
 <span class="re0">$link</span> <span class="sy0">=</span> <span class="kw3">mysql_connect</span><span class="br0">&#40;</span><span class="st_h">'localhost'</span><span class="sy0">,</span> <span class="st_h">'myuser'</span><span class="sy0">,</span> <span class="st_h">'mypassword'</span><span class="br0">&#41;</span><span class="sy0">;</span>
 <span class="kw3">mysql_select_db</span><span class="br0">&#40;</span><span class="st_h">'blog_db'</span><span class="sy0">,</span> <span class="re0">$link</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
 <span class="co1">// SQLクエリを実行する</span>
 <span class="re0">$result</span> <span class="sy0">=</span> <span class="kw3">mysql_query</span><span class="br0">&#40;</span><span class="st_h">'SELECT date, title FROM post'</span><span class="sy0">,</span> <span class="re0">$link</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
 <span class="co1">// ビューのために配列を充填する</span>
 <span class="re0">$posts</span> <span class="sy0">=</span> <span class="kw3">array</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
 <span class="kw1">while</span> <span class="br0">&#40;</span><span class="re0">$row</span> <span class="sy0">=</span> <span class="kw3">mysql_fetch_array</span><span class="br0">&#40;</span><span class="re0">$result</span><span class="sy0">,</span> MYSQL_ASSOC<span class="br0">&#41;</span><span class="br0">&#41;</span>
 <span class="br0">&#123;</span>
    <span class="re0">$posts</span><span class="br0">&#91;</span><span class="br0">&#93;</span> <span class="sy0">=</span> <span class="re0">$row</span><span class="sy0">;</span>
 <span class="br0">&#125;</span>
&nbsp;
 <span class="co1">// 接続を閉じる</span>
 <span class="kw3">mysql_close</span><span class="br0">&#40;</span><span class="re0">$link</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
 <span class="co1">// ビューのスクリプトを読み込む</span>
 <span class="kw1">require</span><span class="br0">&#40;</span><span class="st_h">'view.php'</span><span class="br0">&#41;</span><span class="sy0">;</span></pre>

<p>リスト2-3で示すように、テンプレートのようなPHP構文を含むHTMLコードはビューのスクリプトに保存します。</p>

<p>リスト2-3 - ビューの部分を表す<code>view.php</code></p>

<pre class="php">&lt;html&gt;
  &lt;head&gt;
    &lt;title&gt;投稿の一覧&lt;/title&gt;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;h1&gt;投稿の一覧&lt;/h1&gt;
    &lt;table&gt;
      &lt;tr&gt;&lt;th&gt;日付&lt;/th&gt;&lt;th&gt;タイトル&lt;/th&gt;&lt;/tr&gt;
    <span class="kw2">&lt;?php</span> <span class="kw1">foreach</span> <span class="br0">&#40;</span><span class="re0">$posts</span> <span class="kw1">as</span> <span class="re0">$post</span><span class="br0">&#41;</span><span class="sy0">:</span> <span class="sy1">?&gt;</span>
      &lt;tr&gt;
        &lt;td&gt;<span class="kw2">&lt;?php</span> <span class="kw1">echo</span> <span class="re0">$post</span><span class="br0">&#91;</span><span class="st_h">'date'</span><span class="br0">&#93;</span> <span class="sy1">?&gt;</span>&lt;/td&gt;
        &lt;td&gt;<span class="kw2">&lt;?php</span> <span class="kw1">echo</span> <span class="re0">$post</span><span class="br0">&#91;</span><span class="st_h">'title'</span><span class="br0">&#93;</span> <span class="sy1">?&gt;</span>&lt;/td&gt;
      &lt;/tr&gt;
    <span class="kw2">&lt;?php</span> <span class="kw1">endforeach</span><span class="sy0">;</span> <span class="sy1">?&gt;</span>
    &lt;/table&gt;
  &lt;/body&gt;
&lt;/html&gt;</pre>

<p>ビューが十分に明解であるのか判断するためのよい経験則は、PHPの知識のないHTMLデザイナーが理解できるように最小量のPHPコードだけが含まれるかです。ビューでもっともよく使われるステートメントは<code>echo</code>、<code>if/endif</code>、<code>foreach/endforeach</code>でだけです。また、HTMLタグを<code>echo</code>するPHPコードが存在してはなりません。</p>

<p>すべてのロジックをコントローラースクリプトに移動させます。コントローラー内部にはHTMLが存在しない純粋なPHPコードだけが存在します。当然のことながら、同じコントローラーが全体的に異なるプレゼンテーション、たとえばPDFファイルもしくはXML構造のなかで再利用できるようにする方法を考えるべきです。</p>

<a name="isolating.the.data.manipulation" id="isolating.the.data.manipulation"></a><h4>データの操作機能を分離する</h4>

<p>コントローラースクリプトの大部分はデータの操作専用です。しかし、別のコントローラーのために投稿の一覧表示機能が必要な場合、たとえばblog投稿のRSSフィードを出力するには？コードの重複を避けるために、すべてのデータベースのクエリを一ヶ所に保存したいのであればどうしますか？ <code>post</code>テーブルが<code>weblog_post</code>にリネームされたのでデータモデルを変更することを決定したらどうしますか？MySQLの代わりにPostgreSQLに切り替えたい場合はどうしますか？これらすべてを実現するには、リスト2-4で示されるように、コントローラーからデータ操作のコードを削除し、モデルと呼ばれる別のスクリプトに設置します。</p>

<p>リスト2-4 - モデルの部分を表す<code>model.php</code></p>

<pre class="php"><span class="kw2">function</span> getAllPosts<span class="br0">&#40;</span><span class="br0">&#41;</span>
<span class="br0">&#123;</span>
  <span class="co1">// データベースに接続し、選択する</span>
  <span class="re0">$link</span> <span class="sy0">=</span> <span class="kw3">mysql_connect</span><span class="br0">&#40;</span><span class="st_h">'localhost'</span><span class="sy0">,</span> <span class="st_h">'myuser'</span><span class="sy0">,</span> <span class="st_h">'mypassword'</span><span class="br0">&#41;</span><span class="sy0">;</span>
  <span class="kw3">mysql_select_db</span><span class="br0">&#40;</span><span class="st_h">'blog_db'</span><span class="sy0">,</span> <span class="re0">$link</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
  <span class="co1">// SQLクエリを実行する</span>
  <span class="re0">$result</span> <span class="sy0">=</span> <span class="kw3">mysql_query</span><span class="br0">&#40;</span><span class="st_h">'SELECT date, title FROM post'</span><span class="sy0">,</span> <span class="re0">$link</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
  <span class="co1">// 配列を充填する</span>
  <span class="re0">$posts</span> <span class="sy0">=</span> <span class="kw3">array</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
  <span class="kw1">while</span> <span class="br0">&#40;</span><span class="re0">$row</span> <span class="sy0">=</span> <span class="kw3">mysql_fetch_array</span><span class="br0">&#40;</span><span class="re0">$result</span><span class="sy0">,</span> MYSQL_ASSOC<span class="br0">&#41;</span><span class="br0">&#41;</span>
  <span class="br0">&#123;</span>
     <span class="re0">$posts</span><span class="br0">&#91;</span><span class="br0">&#93;</span> <span class="sy0">=</span> <span class="re0">$row</span><span class="sy0">;</span>
  <span class="br0">&#125;</span>
&nbsp;
  <span class="co1">// 接続を閉じる</span>
  <span class="kw3">mysql_close</span><span class="br0">&#40;</span><span class="re0">$link</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
  <span class="kw1">return</span> <span class="re0">$posts</span><span class="sy0">;</span>
<span class="br0">&#125;</span></pre>

<p>改訂されたコントローラーはリスト2-5で示されています。</p>

<p>リスト2-5 - 改訂されたコントローラーの部分を表す<code>index.php</code></p>

<pre class="php"><span class="co1">// モデルのコードを読み込む</span>
<span class="kw1">require_once</span><span class="br0">&#40;</span><span class="st_h">'model.php'</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
<span class="co1">// 投稿のリストを読みとる</span>
<span class="re0">$posts</span> <span class="sy0">=</span> getAllPosts<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
<span class="co1">// ビューを読み込む</span>
<span class="kw1">require</span><span class="br0">&#40;</span><span class="st_h">'view.php'</span><span class="br0">&#41;</span><span class="sy0">;</span></pre>

<p>これでコントローラーは読みやすくなりました。コントローラーの唯一のタスクはモデルからデータを取得してビューに渡すことです。より複雑なアプリケーションにおいて、コントローラーはリクエスト、ユーザーセッション、認証なども扱うことができます。モデルの関数の名前を明確にすることでコントローラーコードのコメントは不要になります。</p>

<p>モデルスクリプトはデータアクセス専用で、それに応じて編成されます。(リクエストパラメーターのような)データレイヤーに依存しないすべてのパラメーターはコントローラーから渡されなければならず、またモデルによって直接アクセスされないようにしなければなりません。モデルの関数は別のコントローラーで簡単に再利用できません。</p>

<a name="layer.separation.beyond.mvc" id="layer.separation.beyond.mvc"></a><h3>MVCを超えるレイヤーの分離</h3>

<p>MVCアーキテクチャの原則はコードの性質にしたがってコードを3つのレイヤーに分離することです。データロジックのコードはモデルの範囲に、プレゼンテーションのコードはビューの範囲に、アプリケーションのロジックはコントローラーの範囲に設置されます。</p>

<p>デザインパターンを追加することでコードを書く作業が楽になります。モデル、ビュー、コントローラーレイヤーをさらに細かく分割できます。</p>

<a name="database.abstraction" id="database.abstraction"></a><h4>データベースの抽象化</h4>

<p>モデルレイヤーはデータベースアクセスレイヤーとデータベース抽象化レイヤーに分割できます。この方法では、データアクセス関数はデータベースに依存するクエリのステートメントを使いませんが、ほかの関数がそれら自身でクエリを行います。もしあとでデータベースシステムを変更する場合、データベース抽象化レイヤーの更新だけが必要になります。</p>

<p>リスト2-6はデータベース抽象化レイヤーのサンプルで、続くリスト2-7はMySQL固有のデータアクセスレイヤーの例です。</p>

<p>リスト2-6 - モデルの抽象化データベース部分</p>

<pre class="php"><span class="kw2">&lt;?php</span>
&nbsp;
<span class="kw2">function</span> open_connection<span class="br0">&#40;</span><span class="re0">$host</span><span class="sy0">,</span> <span class="re0">$user</span><span class="sy0">,</span> <span class="re0">$password</span><span class="br0">&#41;</span>
<span class="br0">&#123;</span>
  <span class="kw1">return</span> <span class="kw3">mysql_connect</span><span class="br0">&#40;</span><span class="re0">$host</span><span class="sy0">,</span> <span class="re0">$user</span><span class="sy0">,</span> <span class="re0">$password</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="br0">&#125;</span>
&nbsp;
<span class="kw2">function</span> close_connection<span class="br0">&#40;</span><span class="re0">$link</span><span class="br0">&#41;</span>
<span class="br0">&#123;</span>
  <span class="kw3">mysql_close</span><span class="br0">&#40;</span><span class="re0">$link</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="br0">&#125;</span>
&nbsp;
<span class="kw2">function</span> query_database<span class="br0">&#40;</span><span class="re0">$query</span><span class="sy0">,</span> <span class="re0">$database</span><span class="sy0">,</span> <span class="re0">$link</span><span class="br0">&#41;</span>
<span class="br0">&#123;</span>
  <span class="kw3">mysql_select_db</span><span class="br0">&#40;</span><span class="re0">$database</span><span class="sy0">,</span> <span class="re0">$link</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
  <span class="kw1">return</span> <span class="kw3">mysql_query</span><span class="br0">&#40;</span><span class="re0">$query</span><span class="sy0">,</span> <span class="re0">$link</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="br0">&#125;</span>
&nbsp;
<span class="kw2">function</span> fetch_results<span class="br0">&#40;</span><span class="re0">$result</span><span class="br0">&#41;</span>
<span class="br0">&#123;</span>
  <span class="kw1">return</span> <span class="kw3">mysql_fetch_array</span><span class="br0">&#40;</span><span class="re0">$result</span><span class="sy0">,</span> MYSQL_ASSOC<span class="br0">&#41;</span><span class="sy0">;</span>
<span class="br0">&#125;</span></pre>

<p>リスト2-7 - モデルのデータアクセス部分</p>

<pre class="php"><span class="kw2">function</span> getAllPosts<span class="br0">&#40;</span><span class="br0">&#41;</span>
<span class="br0">&#123;</span>
  <span class="co1">// データベースに接続する</span>
  <span class="re0">$link</span> <span class="sy0">=</span> open_connection<span class="br0">&#40;</span><span class="st_h">'localhost'</span><span class="sy0">,</span> <span class="st_h">'myuser'</span><span class="sy0">,</span> <span class="st_h">'mypassword'</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
  <span class="co1">// SQLクエリを実行する</span>
  <span class="re0">$result</span> <span class="sy0">=</span> query_database<span class="br0">&#40;</span><span class="st_h">'SELECT date, title FROM post'</span><span class="sy0">,</span> <span class="st_h">'blog_db'</span><span class="sy0">,</span> <span class="re0">$link</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
  <span class="co1">// 配列を充填する</span>
  <span class="re0">$posts</span> <span class="sy0">=</span> <span class="kw3">array</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
  <span class="kw1">while</span> <span class="br0">&#40;</span><span class="re0">$row</span> <span class="sy0">=</span> fetch_results<span class="br0">&#40;</span><span class="re0">$result</span><span class="br0">&#41;</span><span class="br0">&#41;</span>
  <span class="br0">&#123;</span>
     <span class="re0">$posts</span><span class="br0">&#91;</span><span class="br0">&#93;</span> <span class="sy0">=</span> <span class="re0">$row</span><span class="sy0">;</span>
  <span class="br0">&#125;</span>
&nbsp;
  <span class="co1">// 接続を閉じる</span>
  close_connection<span class="br0">&#40;</span><span class="re0">$link</span><span class="br0">&#41;</span><span class="sy0">;</span>
&nbsp;
  <span class="kw1">return</span> <span class="re0">$posts</span><span class="sy0">;</span>
<span class="br0">&#125;</span></pre>

<p>(リスト2-7で)データベース依存の関数がデータベースアクセスレイヤーで見つからないことを確認できます。これによって関数はデータベースから独立したものになります。これに加えて、データベースの抽象化レイヤーで作成された関数はデータベースにアクセスする必要があるほかの多くのモデル関数で再利用できます。</p>

<blockquote class="note"><p>
  リスト2-6とリスト2-7の例はまだ十分に満足のゆくものではなく、データベースを完全に抽象化するための作業が残されています(データベースから独立したクエリビルダーを通してSQLコードを抽象化すること、すべての関数をクラスに移動させることなど)。しかしこの本の目的はすべてのコードを手で書く方法を示すことではなく、8章でsymfonyがネイティブですべての抽象化を上手に行うことを見ることになります。</p>
</blockquote>

<a name="view.elements" id="view.elements"></a><h4>ビューの要素</h4>

<p>ビューレイヤーもコード分離から恩恵を受けます。アプリケーション全体でWebページが永続する要素を持つことはよくあります: たとえばページヘッダー、グラフィカルなレイアウト、フッター、グローバルナビゲーションなどです。ページの一部のみが変わります。ビューがレイアウトとテンプレートに分離される理由はそういうわけです。通常、レイアウトはアプリケーションもしくはページのグループに対してグローバルです。テンプレートにはコントローラーによって利用可能になる変数のみが含まれます。これらのコンポーネントを連携させるために少々のロジックが必要で、このビューロジックのレイヤーはビューの名前を持ちます。リスト2-8、2-9、2-10で示されるように、これらの原則にしたがって、リスト2-3のビューの部分を3つに分割できます。</p>

<p>リスト2-8 - ビューのテンプレート部分(<code>mytemplate.php</code>)</p>

<pre class="php">&lt;h1&gt;投稿の一覧&lt;/h1&gt;
&lt;table&gt;
&lt;tr&gt;&lt;th&gt;日付&lt;/th&gt;&lt;th&gt;タイトル&lt;/th&gt;&lt;/tr&gt;
<span class="kw2">&lt;?php</span> <span class="kw1">foreach</span> <span class="br0">&#40;</span><span class="re0">$posts</span> <span class="kw1">as</span> <span class="re0">$post</span><span class="br0">&#41;</span><span class="sy0">:</span> <span class="sy1">?&gt;</span>
  &lt;tr&gt;
    &lt;td&gt;<span class="kw2">&lt;?php</span> <span class="kw1">echo</span> <span class="re0">$post</span><span class="br0">&#91;</span><span class="st_h">'date'</span><span class="br0">&#93;</span> <span class="sy1">?&gt;</span>&lt;/td&gt;
    &lt;td&gt;<span class="kw2">&lt;?php</span> <span class="kw1">echo</span> <span class="re0">$post</span><span class="br0">&#91;</span><span class="st_h">'title'</span><span class="br0">&#93;</span> <span class="sy1">?&gt;</span>&lt;/td&gt;
  &lt;/tr&gt;
<span class="kw2">&lt;?php</span> <span class="kw1">endforeach</span><span class="sy0">;</span> <span class="sy1">?&gt;</span>
&lt;/table&gt;</pre>

<p>リスト2-9 - ビューのロジック部分</p>

<pre class="php"><span class="kw2">&lt;?php</span>
&nbsp;
<span class="re0">$title</span> <span class="sy0">=</span> <span class="st_h">'投稿の一覧'</span><span class="sy0">;</span>
<span class="re0">$posts</span> <span class="sy0">=</span> getAllPosts<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span></pre>

<p>リスト2-10 - ビューのレイアウト部分</p>

<pre class="php">&lt;html&gt;
  &lt;head&gt;
    &lt;title&gt;<span class="kw2">&lt;?php</span> <span class="kw1">echo</span> <span class="re0">$title</span> <span class="sy1">?&gt;</span>&lt;/title&gt;
  &lt;/head&gt;
  &lt;body&gt;
    <span class="kw2">&lt;?php</span> <span class="kw1">include</span><span class="br0">&#40;</span><span class="st_h">'mytemplate.php'</span><span class="br0">&#41;</span><span class="sy0">;</span> <span class="sy1">?&gt;</span>
  &lt;/body&gt;
&lt;/html&gt;</pre>

<a name="action.and.front.controller" id="action.and.front.controller"></a><h4>アクションとフロントコントローラー</h4>

<p>前の例ではコントローラーはあまり多くのことを行いませんでしたが、実際のWebアプリケーションでは、コントローラーは多くの仕事を担います。この仕事の重要な部分はアプリケーションのすべてのコントローラーに共通です。共通のタスクにはリクエスト処理、セキュリティ処理、アプリケーション設定の読み込み、および似たような雑用作業が含まれます。コントローラーがフロントコントローラーとアクションによく分割されるのはそういうわけです。フロントコントローラーはアプリケーション全体で唯一のもので、アクションは1つのページに固有のコントローラーコードのみを格納します。</p>

<p>フロントコントローラーの大きな利点の一つはアプリケーション全体で唯一のエントリーポイント(入り口)が提供されることです。アプリケーションを閉鎖することを決定した場合、必要な作業はフロントコントローラーのスクリプトを編集することだけです。フロントコントローラーなしのアプリケーションでは、それぞれのコントローラーをオフに切り替えることが必要です。</p>

<a name="object.orientation" id="object.orientation"></a><h4>オブジェクト指向</h4>

<p>以前のすべての例では手続き型のプログラミングの方法を利用しました。現代のプログラミング言語の手法であるオブジェクト指向プログラミング(OOP)の機能によってプログラミングはさらに簡単なものになります。なぜなら、オブジェクトがロジックをカプセル化し、別のオブジェクトから継承し、クリーンな名前の規約を提供できるからです。</p>

<p>MVCアーキテクチャをオブジェクト指向ではない言語で実装すると名前空間の衝突とコードの重複問題が発生し、全体のコードが読みにくくなります。</p>

<p>オブジェクト指向によって開発者はビューオブジェクト、コントローラーオブジェクト、モデルオブジェクトといったものを扱うことが可能で、以前の例のすべての関数はメソッドに翻訳されます。オブジェクト指向はMVCアーキテクチャのための必須の方法です。</p>

<blockquote class="tip"><p>
  オブジェクト指向の文脈でWebアプリケーションのためのデザインパターンについてもっと学びたい場合、Martin Fowler(マーチン・ファウラー)が書いたPatterns of Enterprise Application Architecture(Addison-Wesley, ISBN: 0-32112-742-0 (邦訳は翔泳社より刊行された「エンタープライズ アプリケーションアーキテクチャパターン」)をお読みください。Fowler本のコードの例はJavaもしくはC#ですが、PHP開発者にも読みやすいです。</p>
</blockquote>

<a name="symfonys.mvc.implementation" id="symfonys.mvc.implementation"></a><h3>symfonyによるMVCの実装方法</h3>

<p>少しお待ちください。blogで投稿の一覧を表示する単独のページに対して、必要なコンポーネントはいくつ存在するでしょうか？ 図2-2で説明されているように、つぎのような部品があります:</p>

<ul>
<li>モデルレイヤー 

<ul>
<li>データベースの抽象化 </li>
<li>データアクセス </li>
</ul></li>
<li>ビューレイヤー 

<ul>
<li>ビュー </li>
<li>テンプレート </li>
<li>レイアウト </li>
</ul></li>
<li>コントローラーレイヤー 

<ul>
<li>フロントコントローラー </li>
<li>アクション</li>
</ul></li>
</ul>

<p>7つのスクリプト、全体の多くは新しいページを作るたびに開いて修正するファイルです！しかしながら、symfonyはこれらの作業を簡単にします。MVCアーキテクチャを最大限活用する一方で、symfonyはこのアーキテクチャを早くて苦痛のともなわないアプリケーションの開発方法で実装します。</p>

<p>最初に、アプリケーションのすべてのアクションでフロントコントローラーとレイアウトは共通です。複数のコントローラーとレイアウトを用意するのは可能ですが、必要なものはそれぞれ1つだけです。フロントコントローラーは純粋なMVCロジックのコンポーネントで、symfonyが生成してくれるので書く必要はありません。</p>

<p>ほかのよいお知らせは、データ構造に基づいて、モデルレイヤーのクラスも自動的に生成されることです。これはPropelライブラリの仕事で、クラスのスケルトンとコードの生成方法を提供します。Propelは外部キー制約もしくはデータフィールドを見つけると、データ操作を簡単にする特別なアクセサー(accessor)メソッドとミューテーター(mutator)メソッドを提供します。そしてデータベースの抽象化は全体的に開発者には見えません。PDO(PHP Data Objects)によってネイティブに処理されるからです。データベースエンジンを変更することを決めた場合、リファクタリングする必要のあるコードはゼロです。設定パラメーターを1つ変更することだけが必要です。</p>

<p>そして最後のお知らせは、プログラミングを行わずに、ビューロジックをシンプルな設定ファイルに簡単に翻訳できることです。</p>

<p>図2-2 - symfonyのワークフロー</p>

<p><img src="images/F0202.png" alt="symfonyのワークフロー" title="symfonyのワークフロー" /></p>

<p>リスト2-11、2-12、2-13で示されるように、このことは私たちの例で説明された投稿の一覧機能をsymfonyで動かすには3つのファイルだけが必要になることを意味します。</p>

<p>リスト2-11 - <code>list</code>アクション(<code>myproject/apps/myapp/modules/weblog/actions/actions.class.php</code>)</p>

<pre class="php"><span class="kw2">&lt;?php</span>
&nbsp;
<span class="kw2">class</span> weblogActions <span class="kw2">extends</span> sfActions
<span class="br0">&#123;</span>
  <span class="kw2">public</span> <span class="kw2">function</span> executeList<span class="br0">&#40;</span><span class="br0">&#41;</span>
  <span class="br0">&#123;</span>
    <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">posts</span> <span class="sy0">=</span> PostPeer<span class="sy0">::</span><span class="me2">doSelect</span><span class="br0">&#40;</span><span class="kw2">new</span> Criteria<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">;</span>
  <span class="br0">&#125;</span>
<span class="br0">&#125;</span></pre>

<p>リスト2-12 - <code>list</code>テンプレート(<code>myproject/apps/myapp/modules/weblog/templates/listSuccess.php</code>)</p>

<pre class="php"><span class="kw2">&lt;?php</span> slot<span class="br0">&#40;</span><span class="st_h">'title'</span><span class="sy0">,</span> <span class="st_h">'投稿の一覧'</span><span class="br0">&#41;</span> <span class="sy1">?&gt;</span>
&nbsp;
&lt;h1&gt;投稿の一覧&lt;/h1&gt;
&lt;table&gt;
&lt;tr&gt;&lt;th&gt;日付&lt;/th&gt;&lt;th&gt;タイトル&lt;/th&gt;&lt;/tr&gt;
<span class="kw2">&lt;?php</span> <span class="kw1">foreach</span> <span class="br0">&#40;</span><span class="re0">$posts</span> <span class="kw1">as</span> <span class="re0">$post</span><span class="br0">&#41;</span><span class="sy0">:</span> <span class="sy1">?&gt;</span>
  &lt;tr&gt;
    &lt;td&gt;<span class="kw2">&lt;?php</span> <span class="kw1">echo</span> <span class="re0">$post</span><span class="sy0">-&gt;</span><span class="me1">getDate</span><span class="br0">&#40;</span><span class="br0">&#41;</span> <span class="sy1">?&gt;</span>&lt;/td&gt;
    &lt;td&gt;<span class="kw2">&lt;?php</span> <span class="kw1">echo</span> <span class="re0">$post</span><span class="sy0">-&gt;</span><span class="me1">getTitle</span><span class="br0">&#40;</span><span class="br0">&#41;</span> <span class="sy1">?&gt;</span>&lt;/td&gt;
  &lt;/tr&gt;
<span class="kw2">&lt;?php</span> <span class="kw1">endforeach</span><span class="sy0">;</span> <span class="sy1">?&gt;</span>
&lt;/table&gt;</pre>

<p>加えて、リスト2-13で示されるように、まだ1つのレイアウトを定義する必要がありますが、これは何度も再利用されます。</p>

<p>リスト2-13 - レイアウト(<code>myproject/apps/myapp/templates/layout.php</code>)</p>

<pre class="php">&lt;html&gt;
  &lt;head&gt;
    &lt;title&gt;<span class="kw2">&lt;?php</span> include_slot<span class="br0">&#40;</span><span class="st_h">'title'</span><span class="br0">&#41;</span> <span class="sy1">?&gt;</span>&lt;/title&gt;
  &lt;/head&gt;
  &lt;body&gt;
    <span class="kw2">&lt;?php</span> <span class="kw1">echo</span> <span class="re0">$sf_content</span> <span class="sy1">?&gt;</span>
  &lt;/body&gt;
&lt;/html&gt;</pre>

<p>これが本当に必要なコードのすべてです。これが以前のリスト2-1で示されたベタ書きのスクリプトとまったく同じページを表示するために必要で正しいコードです。(すべてのコンポーネントを連携させる)残りの部分はsymfonyによって処理されます。行数を数えると、ベタ書きのファイルで書くよりもsymfonyによるMVCアーキテクチャで投稿の一覧機能を作成するほうが時間がかからないもしくはコードを書く作業が少なくてすむことがわかります。そのことだけでなく、symfonyは非常に大きな利点、とりわけきれいなコードの編成、再利用性、柔軟性を提供し、そしてコードを書くのがずっと楽しくなります。おまけに、XHTMLの適合性(conformance)、デバッグ機能、簡単な設定、データベースの抽象化、スマートURLのルーティング、複数の環境、そしてより多くの開発ツールが手に入ります。</p>

<a name="symfony.core.classes" id="symfony.core.classes"></a><h3>symfonyのコアクラス</h3>

<p>symfonyのMVCアーキテクチャはこの本で何度も見ることになるいくつかのクラスを利用します:</p>

<ul>
<li><code>sfController</code>はコントローラークラスです。リクエストを解読してアクションに渡します。 </li>
<li><code>sfRequest</code>はすべてのリクエスト要素(パラメーター、Cookie、ヘッダーなど)を保存します。 </li>
<li><code>sfResponse</code>はレスポンスヘッダーと内容を格納します。これは最終的にHTMLレスポンスに変換されユーザーに送信されるオブジェクトです。 </li>
<li>(<code>sfContext::getInstance()</code>によって読みとりされる)コンテキスト(context)はすべてのコアオブジェクトと現在の設定への参照を保存します: どこからでもアクセス可能です。</li>
</ul>

<p>6章でこれらのオブジェクトを詳しく学びます。</p>

<p>ご覧のとおり、symfonyのすべてのクラスはプレフィックスとして<code>sf</code>を使います。テンプレートのコア変数も同様です。これによってあなた独自のクラスと変数の名前の衝突を回避することが可能で、symfonyのコアクラスは親しみやすく認識しやすいものになります。</p>

<blockquote class="note"><p>
  symfonyで利用されているコーディング規約のなかで、UpperCamelCase(アッパーキャメルケース)はクラスと変数の命名に関する規約です。ただし、2つの例外が存在します: symfonyのコアクラスは小文字の<code>sf</code>で始まり、テンプレートで見つかる変数はアンダースコア(<code>_</code>)で区切られた構文を使います。</p>
</blockquote>

<a name="code.organization" id="code.organization"></a><h2>コードの編成</h2>

<p>これであなたはsymfonyアプリケーションの異なるコンポーネントを理解しましたが、これらがどのように編成されるのかおそらく疑問に思っているでしょう。symfonyはコードを1つのプロジェクト構造に編成して、プロジェクトファイルを1つの標準ツリー構造に設置します。</p>

<a name="project.structure.applications.modules.and.actions" id="project.structure.applications.modules.and.actions"></a><h3>プロジェクトの構造: アプリケーション、モデルとアクション</h3>

<p>symfonyにおいて、プロジェクト(project)とは、同じオブジェクトモデルを共有し、指定ドメインの名前のもとで利用できるサービスとオペレーションの集合です。</p>

<p>プロジェクト内部では、オペレーションはアプリケーションに論理的に分類されます。通常は、アプリケーションは同じプロジェクトのほかのアプリケーションから独立して動作できます。多くの場合、プロジェクトには2つのアプリケーションが収納されます: 1つはフロントオフィス用の、もう1つはバックオフィス用のアプリケーションです。これらは同じデータベースを共有します。しかし、それぞれが異なるアプリケーションである小さなサイトを多く含む1つのプロジェクトを用意することもできます。アプリケーション間のハイパーリンクは絶対パスでなければならないことに注意してください。</p>

<p>それぞれのアプリケーションは1つもしくは複数のモジュールの一式(セット)です。通常、1つのモジュール(module)は同じ目的を持つ1つのページもしくはページのグループを表します。たとえば、<code>home</code>、<code>articles</code>、<code>help</code>、<code>shoppingCart</code>、<code>account</code>モジュールなどを持つことができます</p>

<p>モジュールはアクションを収納します。アクション(action)は1つのモジュールのなかで行うことができるさまざまな行動を表します。たとえば、<code>shoppingCart</code>モジュールは<code>add</code>(追加する)、<code>show</code>(表示する)、<code>update</code>(更新する)アクションを持つことができます。一般的にアクションは動詞として記述できます。アクションの扱いかたは古典的なアプリケーションのページの扱いかたとほとんど同じですが、2つのアクションの結果が同じページになることもあります(たとえば、blogでコメントを投稿画面に追加すると新しいコメントが含まれる投稿画面が再表示される)。</p>

<blockquote class="tip"><p>
  プロジェクトを始めるのに際してアクションが多くの内容を表しすぎる場合、すべてのアクションを単独のモジュールにまとめるのはとても簡単です。アプリケーションがより複雑になるとき、複数のアクションを異なるモジュールに分類する時期です。1章で説明したように、(ふるまいを保ちながら)構造もしくは読みやすさを改善するためにコードを書き直す作業はリファクタリングと呼ばれ、RAD(ラピッドアプリケーション開発)の原則を適用するときに、この作業を多く行います。</p>
</blockquote>

<p>図2-3はプロジェクト/アプリケーション/モジュール/アクション構造のblogプロジェクトのコードの編成のサンプルを示します。しかしプロジェクトの実際のファイルツリー構造は図で示されたセットアップと異なることをご了承してください。</p>

<p>図2-3 - コードの編成の例</p>

<p><img src="images/F0203.png" alt="コードの編成の例" title="コードの編成の例" /></p>

<a name="file.tree.structure" id="file.tree.structure"></a><h3>ファイルのツリー構造</h3>

<p>一般的に、すべてのWebプロジェクトはつぎのような同じ種類の内容を共有します:</p>

<ul>
<li>データベースのファイル。MySQL、PostgreSQLなど</li>
<li>静的なファイル(HTML、画像、JavaScriptファイル、スタイルシートなど)</li>
<li>サイトのユーザーと管理者がアップロードしたファイル</li>
<li>PHPクラスとライブラリ </li>
<li>外部ライブラリ(サードパーティのスクリプト)</li>
<li>バッチファイル(コマンドラインもしくはcron tableで起動するスクリプト) </li>
<li>ログファイル(アプリケーションかつ/もしくはサーバーによって書かれるトレース)</li>
<li>設定ファイル</li>
</ul>

<p>symfonyはアーキテクチャ上の選択(MVCパターンとプロジェクト/アプリケーション/モジュールの分類)と調和した、論理的な方法でこれらすべての内容を編成するファイルの標準ツリー構造を提供します。すべてのプロジェクト、アプリケーションもしくはモジュールを初期化するときに、このツリー構造は自動的に作成されます。もちろん、あなたの都合にあわせてファイルとディレクトリを再編成する、もしくは顧客の要件を満たすためにこのツリー構造を完全にカスタマイズできます。</p>

<a name="root.tree.structure" id="root.tree.structure"></a><h4>rootのツリー構造</h4>

<p>これらはsymfonyのプロジェクトのrootで見つかるディレクトリです:</p>

<pre><code>apps/
  frontend/
  backend/
cache/
config/
data/
  sql/
doc/
lib/
  model/
log/
plugins/
test/
  bootstrap/
  unit/
  functional/
web/
  css/
  images/
  js/
  uploads/
</code></pre>

<p>テーブル2-1でこれらのディレクトリの内容が説明されています。</p>

<p>テーブル2-1 - ルートディレクトリ</p>

<table>
<thead>
<tr>
  <th>ディレクトリ</th>
  <th>説明</th>
</tr>
</thead>
<tbody>
<tr>
  <td><code>apps/</code></td>
  <td>プロジェクトのアプリケーションごとに1つのディレクトリが収納される(よくあるのは<code>frontend</code>と<code>backend</code>でそれぞれはフロントオフィス用とバックオフィス用)</td>
</tr>
<tr>
  <td><code>cache/</code></td>
  <td>設定のキャッシュバージョン、(有効にした場合)アクションのキャッシュバージョン、そしてプロジェクトのテンプレートが収納される。キャッシュメカニズム(詳細は12章)はWebリクエストへのレスポンスを加速するためにこれらのファイルを使う。それぞれのアプリケーションはここにサブディレクトリを収納し、あらかじめ処理されたPHPとHTMLファイルを保存する。</td>
</tr>
<tr>
  <td><code>config/</code></td>
  <td>プロジェクトの一般的な設定が保存される。</td>
</tr>
<tr>
  <td><code>data/</code></td>
  <td>ここでは、プロジェクトのデータファイルを保存できる。たとえば、データベーススキーマ、テーブルを作成するSQLファイル、もしくはSQLiteデータベースファイルなど。</td>
</tr>
<tr>
  <td><code>doc/</code></td>
  <td>プロジェクトのドキュメントが保存される。あなた独自のドキュメントやPHPdocによって生成されたドキュメントも含む。</td>
</tr>
<tr>
  <td><code>lib/</code></td>
  <td>外部クラスもしくは外部ライブラリ専用。ここでは、アプリケーションのあいだで共有が必要なコードを追加できる。<code>model/</code>サブディレクトリはプロジェクトのオブジェクトモデルを保存する(8章で説明)。</td>
</tr>
<tr>
  <td><code>log/</code></td>
  <td>symfonyによって直接生成されたアプリケーションのログファイルを保存する。Webサーバーのログファイル、データベースのログファイル、もしくはプロジェクトの任意部分からのログファイルなども保存できる。symfonyはアプリケーション単位と環境単位で1つのログファイルを作成する(ログファイルは16章で説明)。</td>
</tr>
<tr>
  <td><code>plugins/</code></td>
  <td>アプリケーションにインストールされたプラグインが保存される(プラグインは17章で説明)</td>
</tr>
<tr>
  <td><code>test/</code></td>
  <td>PHPで記述されsymfonyのテストフレームワークと互換性のあるユニットテストと機能テストを保存する(15章で説明)。プロジェクトのセットアップ最中に、symfonyはいくつかの基本的なテストを使ってスタブを自動的に追加する。</td>
</tr>
<tr>
  <td><code>web/</code></td>
  <td>Webサーバーのroot。インターネットからアクセスできるファイルはこのディレクトリに設置されたもののみ。</td>
</tr>
</tbody>
</table>

<a name="application.tree.structure" id="application.tree.structure"></a><h4>アプリケーションのツリー構造</h4>

<p>すべてのアプリケーションディレクトリのツリー構造は同じです:</p>

<pre><code>apps/
  [application name]/
    config/
    i18n/
    lib/
    modules/
    templates/
      layout.php
</code></pre>

<p>テーブル2-2でアプリケーションのサブディレクトリが説明されています。</p>

<p>テーブル2-2 - アプリケーションのサブディレクトリ</p>

<table>
<thead>
<tr>
  <th>ディレクトリ</th>
  <th>説明</th>
</tr>
</thead>
<tbody>
<tr>
  <td><code>config/</code></td>
  <td>たくさんのYAML設定ファイルの一式を収納する。フレームワーク自身で見つかるデフォルトパラメーターとは別に、たいていのアプリケーションの設定が存在する場所。必要な場合、ここでデフォルトパラメーターをさらにオーバーライドできることに注意。5章でアプリケーションの設定について詳しく学ぶ。</td>
</tr>
<tr>
  <td><code>i18n/</code></td>
  <td>アプリケーションの国際化のために使われるファイルが収納される。多くはインターフェイスの翻訳ファイルが収納される(13章で国際化機能を扱う)。国際化のために1つのデータベースを選択した場合、このディレクトリを回避できる。</td>
</tr>
<tr>
  <td><code>lib/</code></td>
  <td>アプリケーションに固有のクラスとライブラリが収納される。</td>
</tr>
<tr>
  <td><code>modules/</code></td>
  <td>アプリケーションの機能を含むすべてのモジュールが収納される。</td>
</tr>
<tr>
  <td><code>templates/</code></td>
  <td>アプリケーションのグローバルテンプレートの一覧が表示される。グローバルテンプレートはすべてのモジュールに共有される。デフォルトでは1つの<code>layout.php</code>ファイルが収納される。このファイルはモジュールテンプレートが挿入されるメインのレイアウト。</td>
</tr>
</tbody>
</table>

<blockquote class="note"><p>
  <code>i18n/</code>、<code>lib/</code>、<code>modules/</code>ディレクトリは新しいアプリケーションのために空です。</p>
</blockquote>

<p>アプリケーションのクラスは同じプロジェクトのほかのアプリケーションのメソッドもしくはプロパティにアクセスできません。同じプロジェクトの2つのアプリケーションのあいだのハイパーリンクは絶対パスでなければならないことを注意してください。1つのプロジェクトを複数のアプリケーションに分割する方法を選ぶとき、この最後の制約に注意する必要があります。</p>

<a name="module.tree.structure" id="module.tree.structure"></a><h4>モジュールのツリー構造</h4>

<p>それぞれのアプリケーションは1つもしくは複数のモジュールを収納します。それぞれのモジュールは独自のサブディレクトリを<code>modules</code>ディレクトリに収納し、このディレクトリの名前はセットアップの最中に選択されます。</p>

<p>典型的なモジュールのツリー構造はつぎのとおりです:</p>

<pre><code>apps/
  [アプリケーションの名前]/
    modules/
      [モジュールの名前]/
          actions/
            actions.class.php
          config/
          lib/
          templates/
            indexSuccess.php
</code></pre>

<p>テーブル2-3でモジュールのサブディレクトリが説明されています。</p>

<p>テーブル2-3 - モジュールのサブディレクトリ</p>

<table>
<thead>
<tr>
  <th>ディレクトリ</th>
  <th>説明</th>
</tr>
</thead>
<tbody>
<tr>
  <td><code>actions/</code></td>
  <td>一般的に<code>action.class.php</code>と呼ばれる単独のファイルが保存される。モジュールのすべてのアクションをこのファイルに保存できる。異なるファイルで1つのモジュールの異なるアクションを書くこともできる。</td>
</tr>
<tr>
  <td><code>config/</code></td>
  <td>モジュール用のローカルパラメーターを持つカスタム設定ファイルを保存できる。</td>
</tr>
<tr>
  <td><code>lib/</code></td>
  <td>モジュール固有のクラスとライブラリを保存できる。</td>
</tr>
<tr>
  <td><code>templates/</code></td>
  <td>モジュールのアクションに対応するテンプレートが収納される。デフォルトのテンプレートの名前は<code>indexSuccess.php</code>で、モジュールのセットアップの間に作成される。</td>
</tr>
</tbody>
</table>

<blockquote class="note"><p>
  <code>config/</code>と<code>lib/</code>ディレクトリは新しいモジュールのために空です。</p>
</blockquote>

<a name="web.tree.structure" id="web.tree.structure"></a><h4>webディレクトリのツリー構造</h4>

<p><code>web</code>ディレクトリは一般利用者がアクセスできるファイル用のディレクトリで、ごくわずかですが制約が存在します。基本的な命名規約に従うことでデフォルトのふるまいと便利なショートカットが提供されます。<code>web</code>ディレクトリ構造の例はつぎのとおりです:</p>

<pre><code>web/
  css/
  images/
  js/
  uploads/
</code></pre>

<p>慣習として、静的なファイルはテーブル2-4の一覧で示されるディレクトリに配置されます。</p>

<p>テーブル 2-4 - 典型的なwebのサブディレクトリ</p>

<table>
<thead>
<tr>
  <th>ディレクトリ</th>
  <th>説明</th>
</tr>
</thead>
<tbody>
<tr>
  <td><code>css/</code></td>
  <td><code>.css</code>拡張子を持つスタイルシート</td>
</tr>
<tr>
  <td><code>images/</code></td>
  <td><code>.jpg</code>、<code>.png</code>もしくは<code>.gif</code>形式の画像が保存される</td>
</tr>
<tr>
  <td><code>js/</code></td>
  <td><code>.js</code>拡張子を持つJavaScriptファイルが保存される</td>
</tr>
<tr>
  <td><code>uploads/</code></td>
  <td>ユーザーがアップロードしたファイルを保存できる。通常このディレクトリには画像が保存されるが、開発サーバーと運用サーバーの同期化がアップロードされた画像に影響を与えないようにimagesディレクトリとは区別される。</td>
</tr>
</tbody>
</table>

<blockquote class="note"><p>
  デフォルトのツリー構造の維持は大いに推奨されますが、特定のニーズのために修正することは可能です。たとえば、異なるツリー構造とコーディング規約を持つサーバーでプロジェクトを運営できるようにするためなどです。ファイルのツリー構造の修正に関する詳細な情報は19章を参照してください。</p>
</blockquote>

<a name="common.instruments" id="common.instruments"></a><h2>共通の手法</h2>

<p>symfonyの開発のなかで少数のテクニックが繰り返し使われ、この本とあなた独自のプロジェクトでもこれらをよく目にすることになります。これらはパラメーターホルダー、定数、そしてクラスのオートロードを含みます。</p>

<a name="parameter.holders" id="parameter.holders"></a><h3>パラメーターホルダー</h3>

<p>symfonyの多くのクラスは1つのパラメーターホルダー(parameter holder)を持ちます。これはゲッター(getter)とセッター(setter)メソッドを持つプロパティをカプセル化するために便利な方法です。たとえば、<code>sfRequest</code>クラスは<code>getParameterHolder()</code>メソッドを呼び出すことで読みとりできるパラメーターホルダーを持ちます。リスト2-14で示されるように、それぞれのパラメーターホルダーは同じ方法でデータを保存します。</p>

<p>リスト2-14 - <code>sfRequest</code>パラメーターホルダーを使う</p>

<pre class="php"><span class="re0">$request</span><span class="sy0">-&gt;</span><span class="me1">getParameterHolder</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span><span class="me1">set</span><span class="br0">&#40;</span><span class="st_h">'foo'</span><span class="sy0">,</span> <span class="st_h">'bar'</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="kw1">echo</span> <span class="re0">$request</span><span class="sy0">-&gt;</span><span class="me1">getParameterHolder</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">-&gt;</span><span class="me1">get</span><span class="br0">&#40;</span><span class="st_h">'foo'</span><span class="br0">&#41;</span><span class="sy0">;</span>
 <span class="sy0">=&gt;</span> <span class="st_h">'bar'</span></pre>

<p>パラメーターホルダーを使う多くのクラスはget/setオペレーションのために必要なコードを短くするプロキシメソッド(proxy method)を提供します。これは<code>sfRequest</code>オブジェクトにもあてはまり、リスト2-15のコードはリスト2-14と同じことができます。</p>

<p>リスト2-15 - <code>sfRequest</code>パラメーターホルダーのプロキシメソッドを使う</p>

<pre class="php"><span class="re0">$request</span><span class="sy0">-&gt;</span><span class="me1">setParameter</span><span class="br0">&#40;</span><span class="st_h">'foo'</span><span class="sy0">,</span> <span class="st_h">'bar'</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="kw1">echo</span> <span class="re0">$request</span><span class="sy0">-&gt;</span><span class="me1">getParameter</span><span class="br0">&#40;</span><span class="st_h">'foo'</span><span class="br0">&#41;</span><span class="sy0">;</span>
 <span class="sy0">=&gt;</span> <span class="st_h">'bar'</span></pre>

<p>パラメーターホルダーのゲッターは2番目の引数をデフォルト値として受けとります。これは条件文で実現できることよりもはるかに簡潔で便利なフォールバックメカニズム(訳注：障害が起きても最低限の機能を維持するメカニズム)を提供します。具体例はリスト2-16をご覧ください。</p>

<p>リスト2-16 - パラメーターホルダーのゲッターのデフォルト値を使う</p>

<pre class="php"><span class="co1">// 'foobar' パラメーターは定義されていないので、ゲッターは空の値を返す</span>
<span class="kw1">echo</span> <span class="re0">$request</span><span class="sy0">-&gt;</span><span class="me1">getParameter</span><span class="br0">&#40;</span><span class="st_h">'foobar'</span><span class="br0">&#41;</span><span class="sy0">;</span>
 <span class="sy0">=&gt;</span> <span class="kw4">null</span>
&nbsp;
<span class="co1">// デフォルト値はゲッターを条件文に設置することで使える</span>
<span class="kw1">if</span> <span class="br0">&#40;</span><span class="re0">$request</span><span class="sy0">-&gt;</span><span class="me1">hasParameter</span><span class="br0">&#40;</span><span class="st_h">'foobar'</span><span class="br0">&#41;</span><span class="br0">&#41;</span>
<span class="br0">&#123;</span>
  <span class="kw1">echo</span> <span class="re0">$request</span><span class="sy0">-&gt;</span><span class="me1">getParameter</span><span class="br0">&#40;</span><span class="st_h">'foobar'</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="br0">&#125;</span>
<span class="kw1">else</span>
<span class="br0">&#123;</span>
  <span class="kw1">echo</span> <span class="st_h">'default'</span><span class="sy0">;</span>
<span class="br0">&#125;</span>
 <span class="sy0">=&gt;</span> <span class="kw1">default</span>
&nbsp;
<span class="co1">// しかしそれに対してゲッターの2番目の引数を使うほうがはるかに速い</span>
<span class="kw1">echo</span> <span class="re0">$request</span><span class="sy0">-&gt;</span><span class="me1">getParameter</span><span class="br0">&#40;</span><span class="st_h">'foobar'</span><span class="sy0">,</span> <span class="st_h">'default'</span><span class="br0">&#41;</span><span class="sy0">;</span>
 <span class="sy0">=&gt;</span> <span class="kw1">default</span></pre>

<p>(<code>sfNamespacedParameterHolder</code>クラスのおかげで)symfonyのコアクラスのなかには名前空間をサポートするものもあります。3番目の引数をセッターもしくはゲッターに指定する場合、これは名前空間として使われ、パラメーターはその名前空間でのみ定義されます。リスト2-17で例が示されています。</p>

<p>リスト2-17 - <code>sfUser</code>パラメーターホルダーの名前空間を使う</p>

<pre class="php"><span class="re0">$user</span><span class="sy0">-&gt;</span><span class="me1">setAttribute</span><span class="br0">&#40;</span><span class="st_h">'foo'</span><span class="sy0">,</span> <span class="st_h">'bar1'</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="re0">$user</span><span class="sy0">-&gt;</span><span class="me1">setAttribute</span><span class="br0">&#40;</span><span class="st_h">'foo'</span><span class="sy0">,</span> <span class="st_h">'bar2'</span><span class="sy0">,</span> <span class="st_h">'my/name/space'</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="kw1">echo</span> <span class="re0">$user</span><span class="sy0">-&gt;</span><span class="me1">getAttribute</span><span class="br0">&#40;</span><span class="st_h">'foo'</span><span class="br0">&#41;</span><span class="sy0">;</span>
 <span class="sy0">=&gt;</span> <span class="st_h">'bar1'</span>
<span class="kw1">echo</span> <span class="re0">$user</span><span class="sy0">-&gt;</span><span class="me1">getAttribute</span><span class="br0">&#40;</span><span class="st_h">'foo'</span><span class="sy0">,</span> <span class="kw4">null</span><span class="sy0">,</span> <span class="st_h">'my/name/space'</span><span class="br0">&#41;</span><span class="sy0">;</span>
 <span class="sy0">=&gt;</span> <span class="st_h">'bar2'</span></pre>

<p>もちろん、構文のファシリティを利用するために独自クラスにパラメーターホルダーを追加することもできます。リスト2-18はパラメーターホルダーでクラスを定義する方法を示しています。</p>

<p>リスト2-18 - 独自クラスにパラメーターホルダーを追加する</p>

<pre class="php"><span class="kw2">class</span> MyClass
<span class="br0">&#123;</span>
  protected <span class="re0">$parameterHolder</span> <span class="sy0">=</span> <span class="kw4">null</span><span class="sy0">;</span>
&nbsp;
  <span class="kw2">public</span> <span class="kw2">function</span> initialize<span class="br0">&#40;</span><span class="re0">$parameters</span> <span class="sy0">=</span> <span class="kw3">array</span><span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#41;</span>
  <span class="br0">&#123;</span>
    <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">parameterHolder</span> <span class="sy0">=</span> <span class="kw2">new</span> sfParameterHolder<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span>
    <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">parameterHolder</span><span class="sy0">-&gt;</span><span class="me1">add</span><span class="br0">&#40;</span><span class="re0">$parameters</span><span class="br0">&#41;</span><span class="sy0">;</span>
  <span class="br0">&#125;</span>
&nbsp;
  <span class="kw2">public</span> <span class="kw2">function</span> getParameterHolder<span class="br0">&#40;</span><span class="br0">&#41;</span>
  <span class="br0">&#123;</span>
    <span class="kw1">return</span> <span class="re0">$this</span><span class="sy0">-&gt;</span><span class="me1">parameterHolder</span><span class="sy0">;</span>
  <span class="br0">&#125;</span>
<span class="br0">&#125;</span></pre>

<a name="constants" id="constants"></a><h3>定数</h3>

<p>定数は一度定義すると変更できないのでsymfonyでは定数は見つかりません。symfonyは<code>sfConfig</code>と呼ばれる独自の設定オブジェクトを使い、これは定数を置き換えます。このオブジェクトはどこからでもパラメーターにアクセスできるstaticメソッドを提供します。リスト2-19は<code>sfConfig</code>クラスのメソッドの使いかたを示しています。</p>

<p>リスト2-19 - 定数の代わりに<code>sfConfig</code>クラスのメソッドを使う</p>

<pre class="php"><span class="co1">// PHP定数の代わり</span>
<span class="kw3">define</span><span class="br0">&#40;</span><span class="st_h">'FOO'</span><span class="sy0">,</span> <span class="st_h">'bar'</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="kw1">echo</span> FOO<span class="sy0">;</span>
&nbsp;
<span class="co1">// symfonyはsfConfigオブジェクトを使う</span>
sfConfig<span class="sy0">::</span><span class="me2">set</span><span class="br0">&#40;</span><span class="st_h">'foo'</span><span class="sy0">,</span> <span class="st_h">'bar'</span><span class="br0">&#41;</span><span class="sy0">;</span>
<span class="kw1">echo</span> sfConfig<span class="sy0">::</span><span class="me2">get</span><span class="br0">&#40;</span><span class="st_h">'foo'</span><span class="br0">&#41;</span><span class="sy0">;</span></pre>

<p><code>sfConfig</code>メソッドはデフォルト値をサポートし、値を変更するために同じパラメーターで<code>sfConfig::set()</code>メソッドを何度も呼び出すことができます。5章で<code>sfConfig</code>メソッドの詳細を検討します。</p>

<a name="class.autoloading" id="class.autoloading"></a><h3>クラスのオートロード機能</h3>

<p>通常の方法では、PHPでクラスを使うもしくはオブジェクトを作るとき、まずはクラスの定義をインクルードする必要があります。</p>

<pre class="php"><span class="kw1">include</span> <span class="st_h">'classes/MyClass.php'</span><span class="sy0">;</span>
<span class="re0">$myObject</span> <span class="sy0">=</span> <span class="kw2">new</span> MyClass<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span></pre>

<p>しかし、多くのクラスと深いディレクトリ構造をかかえる大きなプロジェクトにおいて、インクルードするすべてのクラスファイルとそれらのパスを追跡するために多くの時間が必要です。<code>spl_autoload_register()</code>関数を提供することで、symfonyは<code>include</code>ステートメントを不要にするので、つぎのようにクラスを直接書けます:</p>

<pre class="php"><span class="re0">$myObject</span> <span class="sy0">=</span> <span class="kw2">new</span> MyClass<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="sy0">;</span></pre>

<p>symfonyはプロジェクトの<code>lib/</code>ディレクトリの1つのなかに存在する<code>php</code>拡張子で終わるすべてのファイルのなかの<code>MyClass</code>の定義を探します。クラスの定義が見つかると、ファイルは自動的にインクルードされます。</p>

<p>すべてのクラスを<code>lib/</code>ディレクトリに保存する場合、クラスをインクルードしなくてすみます。symfonyプロジェクトが通常<code>include</code>ステートメントもしくは<code>require</code>ステートメントを必要としない理由はそういうわけです。</p>

<blockquote class="note"><p>
  パフォーマンスをさらに改善するために、symfonyのオートロード機能は最初のリクエストの間に(内部の設定ファイルで定義された)ディレクトリのリストをスキャンします。symfonyはこれらのディレクトリが収納するすべてのクラスを登録し、PHPファイルに対応するクラス/ファイルを連想配列として保存します。この方法によって、今後のリクエストではディレクトリのスキャンを行う必要はありません。クラスファイルをプロジェクトに追加もしくは移動させるたびに<code>symfony cache:clear</code>コマンドを呼び出してキャッシュをクリアする必要がある理由はそういうわけです(開発環境を除いて、クラスが見つからないときsymfonyは一度キャッシュをクリアします)。キャッシュは12章で、オートロード機能の設定は19章で学ぶことになります。</p>
</blockquote>

<a name="summary" id="summary"></a><h2>まとめ</h2>

<p>MVCフレームワーク(Model-View-Controller framework)を利用することで、フレームワークの規約にしたがってコードを分割して編成することが強制されます。プレゼンテーションのコードはビュー(View)に、データの操作コードはモデル(Model)に、リクエストの操作ロジックはコントローラー(Controller)になります。これによってMVCパターンのアプリケーションはとても便利で非常に制限されたものになります。</p>

<p>symfonyはPHP 5で書かれたMVCフレームワークです。その構造はMVCパターンを乗り越えながらも、とても使いやすいように設計さされています。器用で設定が柔軟であるおかげで、すべてのWebアプリケーションのプロジェクトにsymfonyを合わせることができます。</p>

<p>今、あなたはsymfonyの背後に存在する基本理論を理解したので、最初のアプリケーションを開発する準備がほとんど整いました。しかしそのまえに、symfonyをインストールして開発サーバーで動かすことが必要です。</p>
</div>
<div class="navigation">
<hr/>
<table width="100%">
<tr>
<td width="40%" align="left"><a href="01-Introducing-Symfony.html">前の章</a></td>
<td width="20%" align="center"><a href="index.html">ホーム</a></td>
<td width="40%" align="right"><a href="03-Running-Symfony.html">次の章</a></td>
</tr>
</table>

</div>
</body>

</html>
